package com.tenton.util;

import com.tenton.annotation.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.List;

/**
 * @ClassName：XmlUtil
 * @Description：Xml工具类
 * @Author：tangdong
 * @Date：2022/11/7 10:10
 **/
@Slf4j
public class XmlUtil {

    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static <T> T readXml(String xmlPath, Class<T> tClass) {
        try {
            Field[] fields = tClass.getDeclaredFields();
            File file = new File(xmlPath);
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse(file);
            T t = tClass.newInstance();
            for (Field field : fields) {
                field.setAccessible(true);
                String name = getTagName(field);
                String nodeValue = doc.getElementsByTagName(name).item(0).getFirstChild().getNodeValue();
                field.set(t, nodeValue.trim());
            }
            return t;
        } catch (ParserConfigurationException e) {
            log.error("Xml工具类构造文档构造器失败！");
            e.printStackTrace();
        } catch (IOException e) {
            log.error("Xml工具类文件转换文档失败！");
            e.printStackTrace();
        } catch (SAXException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            log.error("Xml工具类通过反射创建类失败！");
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return null;
    }

    /**
     * 读取子节点包含对象的XML
     *
     * @param xmlPath
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T readObjectXml(String xmlPath, Class<T> clazz) {
        BufferedReader br;
        try {
            br = new BufferedReader(new FileReader(xmlPath));
            JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
            T t = (T)unmarshaller.unmarshal(br);
            return t;
        } catch (FileNotFoundException e) {
            log.error("文件未找到！");
            throw new RuntimeException(e);
        } catch (JAXBException e) {
            log.error("通过反射创建JAXBContext失败！");
            throw new RuntimeException(e);
        }
    }

    /**
     * 存储XML
     *
     * @param model
     * @param xmlPath
     * @param <T>
     */
    public static <T> void saveXml(T model, String xmlPath) {
        File file = new File(xmlPath);
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document document = builder.newDocument();
            document.setXmlStandalone(true);
            Element element = writeDocument(document, model, 0);
            document.appendChild(element);
            TransformerFactory tff = TransformerFactory.newInstance();
            Transformer tf = tff.newTransformer();
            tf.setOutputProperty(OutputKeys.INDENT, "yes");
            tf.transform(new DOMSource(document), new StreamResult(file));
        } catch (ParserConfigurationException e) {
            log.error("XML工具类文档构造器构造失败！");
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            log.error("XML工具类获取对象字段名失败！");
            e.printStackTrace();
        } catch (TransformerConfigurationException e) {
            log.error("XML工具类通过反射创建转换器失败！");
            e.printStackTrace();
        } catch (TransformerException e) {
            log.error("XML工具类转换文档失败！");
            e.printStackTrace();
        }
    }

    /**
     * 存储List对象到XML
     *
     * @param models
     * @param xmlPath
     * @param <T>
     */
    public static <T> void saveListToXml(List<T> models, String xmlPath) {
        File file = new File(xmlPath);
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.newDocument();
            doc.setXmlStandalone(true);
            Class<?> fatherClass = models.getClass();
            Element fatherElement = null;
            ElementName fatherNode = fatherClass.getAnnotation(ElementName.class);
            if (!ObjectUtils.isEmpty(fatherNode)) {
                fatherElement = doc.createElement(fatherNode.name());
            } else {
                fatherElement = doc.createElement(fatherClass.getSimpleName());
            }
            XmlAttribute[] attributes = fatherClass.getAnnotationsByType(XmlAttribute.class);
            if (!ObjectUtils.isEmpty(attributes)) {
                for (XmlAttribute attribute : attributes) {
                    if ("DataIndexCount".equals(attribute.attrKey())) {
                        fatherElement.setAttribute(attribute.attrKey(), String.valueOf(models.size()));
                    } else {
                        fatherElement.setAttribute(attribute.attrKey(), attribute.attrValue());
                    }
                }
            }
            for (int i = 0; i < models.size(); i++) {
                Element childElement = writeDocument(doc, models.get(i), i);
                fatherElement.appendChild(childElement);
            }
            doc.appendChild(fatherElement);

            TransformerFactory tff = TransformerFactory.newInstance();
            Transformer tf = tff.newTransformer();
            tf.setOutputProperty(OutputKeys.INDENT, "yes");
            tf.transform(new DOMSource(doc), new StreamResult(file));
        } catch (ParserConfigurationException e) {
            log.error("XML工具类文档构造器构造失败！");
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            log.error("XML工具类获取对象字段名失败！");
            e.printStackTrace();
        } catch (TransformerConfigurationException e) {
            log.error("XML工具类通过反射创建转换器失败！");
            e.printStackTrace();
        } catch (TransformerException e) {
            log.error("XML工具类转换文档失败！");
            e.printStackTrace();
        }
    }

    /**
     * 存写文档
     *
     * @param document
     * @param obj
     * @param i
     */
    private static Element writeDocument(Document document, Object obj, int i) throws IllegalAccessException {
        Class<?> aClass = obj.getClass();
        Element childElement = null;
        ElementName childName = aClass.getAnnotation(ElementName.class);
        if (!ObjectUtils.isEmpty(childName)) {
            childElement = document.createElement(childName.name());
        } else {
            childElement = document.createElement(aClass.getSimpleName());
        }
        XmlAttribute[] annotations = aClass.getAnnotationsByType(XmlAttribute.class);
        if (!ObjectUtils.isEmpty(annotations)) {
            for (XmlAttribute annotation : annotations) {
                if ("DataIndex".equals(annotation.attrKey())) {
                    childElement.setAttribute(annotation.attrKey(), String.valueOf(i));
                } else {
                    childElement.setAttribute(annotation.attrKey(), annotation.attrValue());
                }
            }
        }
        Field[] fields = aClass.getDeclaredFields();
        for (int j = 0; j < fields.length; j++) {
            Field field = fields[j];
            field.setAccessible(true);
            if (!field.isAnnotationPresent(NoSave.class)) {
                //子类标签名
                Element grandElement = null;
                if (field.isAnnotationPresent(ObjectEnable.class)) {
                    grandElement = writeDocument(document, field.get(obj), i);
                } else {
                    String name = getTagName(field);
                    grandElement = document.createElement(name);
                    if (field.isAnnotationPresent(DateEnable.class)) {
                        grandElement.setTextContent(sdf.format(field.get(obj)));
                    } else {
                        grandElement.setTextContent(field.get(obj) == null ? "" : field.get(obj).toString());
                    }
                }
                childElement.appendChild(grandElement);
            }
        }
        return childElement;
    }

    /**
     * 获取标签名
     *
     * @param field
     * @return
     */
    private static String getTagName(Field field) {
        field.setAccessible(true);
        String name = field.getName();
        String start = name.substring(0, 1);
        String end = name.substring(1);
        return start.toUpperCase().concat(end);
    }
}
